using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using RegionInterface;

namespace RegionCore
{
    public delegate void SocketConnect(Socket client);
    public delegate void SocketMessage(SocketEntry client, DataObject[] data);
    public delegate void SocketDisConnect(SocketEntry client, SocketException e);
    public class ServerSocket
    {
        private const string POLICY_STRING = "<policy-file-request/>";
        private byte[] arrPolicyServerFile = Encoding.UTF8.GetBytes(@"<?xml version='1.0'?>
<cross-domain-policy>
	<allow-access-from domain=""*"" to-ports=""79-81"" />
</cross-domain-policy>");

        private int ServerPort = 11000;
        private Socket listener;
        public event SocketConnect OnSocketConnect;
        public event SocketMessage OnSocketMessage;
        public event SocketDisConnect OnSocketDisConnect;
        private SocketAsyncEventArgs AcceptArgs = new SocketAsyncEventArgs();
        private void SocketConnect(Socket client)
        {
            if (OnSocketConnect != null) OnSocketConnect(client);
        }
        private void SocketMessage(SocketEntry client, DataObject[] data)
        {
            if (OnSocketMessage != null) OnSocketMessage(client, data);
        }
        private void SocketDisConnect(SocketEntry client, SocketException e)
        {
            if (OnSocketDisConnect != null && client != null)
            {
                try
                {
                    if (client != null)
                    {
                        OnSocketDisConnect(client, e);
                    }
                }
                catch (Exception ex)
                {
                    Log.Error(ex.ToString());
                }
            }
        }
        public ServerSocket(int port)
        {
            ServerPort = port;
        }
        public void StartListening()
        {
            IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, ServerPort);
            listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                listener.Bind(localEndPoint);
                listener.Listen(100);

                //listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
                AcceptArgs.Completed += AcceptCallback;
                listener.AcceptAsync(AcceptArgs);
            }
            catch (Exception e)
            {
                Log.Error(e.ToString());
            }
        }


        private void AcceptCallback(object AcceptSender, SocketAsyncEventArgs Accept)
        {
            if (Accept.SocketError == SocketError.Success)
            {
                Socket handler = Accept.AcceptSocket;
                SocketConnect(handler);

                SocketEntry entry = new SocketEntry(handler);
                entry.ReceiveEventArgs.Completed += delegate(object sender, SocketAsyncEventArgs e)
                {
                    try
                    {
                        if (entry.HasPolicy)
                        {
                            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
                            {
                                entry.ReceiveLength = e.BytesTransferred;
                                SocketMessage(entry, entry.DataPool.BufferToObject(entry.ReceiveBuffer, entry.ReceiveLength));
                                entry.Client.ReceiveAsync(e);
                            }
                            else
                            {
                                SocketDisConnect(entry, new SocketException(10054));
                            }
                        }
                        else
                        {
                            entry.HasPolicy = true;
                            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
                            {
                                string policyRequest = UTF8Encoding.UTF8.GetString(entry.ReceiveBuffer, 0, e.BytesTransferred);
                                if (string.Compare(policyRequest, POLICY_STRING) == 0) entry.Client.Send(arrPolicyServerFile);
                                else
                                {
                                    entry.ReceiveLength = e.BytesTransferred;
                                    SocketMessage(entry, entry.DataPool.BufferToObject(entry.ReceiveBuffer, entry.ReceiveLength));
                                }
                                entry.Client.ReceiveAsync(e);
                            }
                            else
                            {
                                SocketDisConnect(entry, new SocketException(10054));
                            }
                        }
                    }
                    catch (Exception ex)//never run here...
                    {
                        SocketDisConnect(entry, new SocketException(10054));
                    }
                };
                entry.Client.ReceiveAsync(entry.ReceiveEventArgs);
            }
            AcceptArgs.AcceptSocket = null;
            listener.AcceptAsync(AcceptArgs);
        }
        public void Send(SocketEntry handler, DataObject data)
        {
            try
            {
                //if (handler.Client.Connected) handler.Client.Send(data.Buffer, 0, data.Length, SocketFlags.None);
                handler.SendData(data);
            }
            catch { }
        }
        public void CloseAndDispose(Socket s)
        {
            try
            {
                if (s != null)
                {
                    s.Shutdown(SocketShutdown.Both);
                    s.Close();
                }
            }
            catch (Exception e)
            {
                Log.Error(e.ToString());
            }
        }


    }
}
